package common;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * A XML parser which provides related XML parsing methods
 * 
 * @author xijiang
 * @since 2011.12.29
 * @version 1.0
 */

public class XMLParserUtil {

	private static DocumentBuilderFactory domfac;
	private static DocumentBuilder dombuilder;
	private static XMLParserUtil instance;
	private static TransformerFactory tranFactory;
	private static Transformer aTransformer;

	private XMLParserUtil() {
		domfac = DocumentBuilderFactory.newInstance();
		tranFactory = TransformerFactory.newInstance();
		try {
			dombuilder = domfac.newDocumentBuilder();
			aTransformer = tranFactory.newTransformer();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		}
	}

	public static XMLParserUtil getInstance() {
		if (instance == null) {
			instance = new XMLParserUtil();
		}
		return instance;
	}

	public Document getDocument(String xmlPath) {
		if (dombuilder == null) {
			try {
				dombuilder = domfac.newDocumentBuilder();
			} catch (ParserConfigurationException e) {
				e.printStackTrace();
			}
		}

		FileInputStream fis = null;
		try {
			fis = new FileInputStream(xmlPath);
		} catch (FileNotFoundException e) {
			System.out.println("The specified xml file path is not valid!");
		}

		Document doc = null;
		try {
			doc = dombuilder.parse(fis);
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return doc;
	}

	//Get the string format of the xml document except the xml declaration line
	public String getXMLString(Document doc) {
		String ret;
		Source src = new DOMSource(doc);
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		Result dest = new StreamResult(outputStream);
		try {
			aTransformer.transform(src, dest);
		} catch (TransformerException e) {
			e.printStackTrace();
			return null;
		}

		ret = outputStream.toString();
		if (ret.startsWith("<?")) {
			return ret.substring(ret.indexOf(">") + 1);// Ignore the xml
														// declaration line
		}
		return ret;
	}

	// Get the xml root element
	public Element getRootElem(Document doc) {
		return doc.getDocumentElement();
	}

	//Get elem's elements with tag name tagName
	public NodeList getElements(Element elem, String tagName) {
		if(elem==null) {
			return null;
		}
		
		return elem.getElementsByTagName(tagName);
	}
	
	//Get all the child element nodes of a parent node and the child node's nodeName is nodeName
	public ArrayList<Node> getChildElementNodesByName(Node node, String nodeName) {
		
		if(node==null) {
			return null;
		}
		
		ArrayList<Node> list = new ArrayList<Node>();
		NodeList nodeList = node.getChildNodes();
		Node temp = null;
		for(int i=0;i<nodeList.getLength();i++) {
			temp = nodeList.item(i);
			if(temp.getNodeType()==Node.ELEMENT_NODE && temp.getNodeName().equals(nodeName)) {
				list.add(temp);
			}
		}
		
		return list;
	}
	
	//Get the first child element node of parent node and the child node's nodeName is nodeName
	public Node getChildElemNodeByName(Node node, String nodeName) {
		
		if(node==null) {
			return null;
		}
		
		NodeList nodeList = node.getChildNodes();
		Node temp = null;
		for(int i=0;i<nodeList.getLength();i++) {
			temp = nodeList.item(i);
			if(temp.getNodeType()==Node.ELEMENT_NODE && temp.getNodeName().equals(nodeName)) {
				break;
			}
		}
		
		return temp;
	}
	
	// Get child node list of the given node, and the node in the list should be in nodeType
	public ArrayList<Node> getChildNodeByNodeType(Node node, int nodeType) {
		ArrayList<Node> rsList = new ArrayList<Node>();
		NodeList childNodeList = node.getChildNodes();
		for(int j=0;j<childNodeList.getLength();j++) {
			if(childNodeList.item(j).getNodeType() == nodeType) {
				rsList.add(childNodeList.item(j));
			}
		}
		
		return rsList;
	}
	
	//Get the text content of node
	public String getNodeText(Node node) {
		if(node==null)
			return null;
		return node.getTextContent();
	}

	// Get the text content of the first tag specified by tagName
	public String getFirstTagText(String xmlString, String tagName) {
		try {
			StringReader sr = new StringReader(xmlString);
			InputSource is = new InputSource(sr);
			Document doc = dombuilder.parse(is);
			return doc.getElementsByTagName(tagName).item(0).getTextContent();
		} catch (Exception e) {
			return null;
		}
	}
	
	//Get attribute value of the node specified by the attribute's name attrName
	public String getAttrValByName(Node node, String attrName) {
		if(node==null || attrName==null || attrName.trim().equals("")) {
			return null;
		}
		
		return node.getAttributes().getNamedItem(attrName).getNodeValue();
	}

}
